#include <efsw/WatcherWin32.hpp>
#include <efsw/String.hpp>

#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32

namespace efsw
{

/// Unpacks events and passes them to a user defined callback.
void CALLBACK WatchCallback(DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
	if (dwNumberOfBytesTransfered == 0 || NULL == lpOverlapped)
	{
		return;
	}

	char szFile[MAX_PATH];
	PFILE_NOTIFY_INFORMATION pNotify;
	WatcherStructWin32 * tWatch = (WatcherStructWin32*) lpOverlapped;
	WatcherWin32 * pWatch = tWatch->Watch;
	size_t offset = 0;

	do
	{
		bool skip = false;

		pNotify = (PFILE_NOTIFY_INFORMATION) &pWatch->Buffer[offset];
		offset += pNotify->NextEntryOffset;

		int count = WideCharToMultiByte(CP_UTF8, 0, pNotify->FileName,
			pNotify->FileNameLength / sizeof(WCHAR),
			szFile, MAX_PATH - 1, NULL, NULL);
		szFile[count] = TEXT('\0');

		std::string nfile( szFile );

		if ( FILE_ACTION_MODIFIED == pNotify->Action )
		{
			FileInfo fifile( std::string( pWatch->DirName ) + nfile );

			if ( pWatch->LastModifiedEvent.file.ModificationTime == fifile.ModificationTime && pWatch->LastModifiedEvent.file.Size == fifile.Size && pWatch->LastModifiedEvent.fileName == nfile )
			{
				skip = true;
			}

			pWatch->LastModifiedEvent.fileName	= nfile;
			pWatch->LastModifiedEvent.file		= fifile;
		}

		if ( !skip )
		{
			pWatch->Watch->handleAction(pWatch, nfile, pNotify->Action);
		}
	} while (pNotify->NextEntryOffset != 0);

	if (!pWatch->StopNow)
	{
		RefreshWatch(tWatch);
	}
}

/// Refreshes the directory monitoring.
bool RefreshWatch(WatcherStructWin32* pWatch)
{
	return ReadDirectoryChangesW(
				pWatch->Watch->DirHandle,
				pWatch->Watch->Buffer,
				sizeof(pWatch->Watch->Buffer),
				pWatch->Watch->Recursive,
				pWatch->Watch->NotifyFilter,
				NULL,
				&pWatch->Overlapped,
				NULL
	) != 0;
}

/// Stops monitoring a directory.
void DestroyWatch(WatcherStructWin32* pWatch)
{
	if (pWatch)
	{
		WatcherWin32 * tWatch = pWatch->Watch;
		tWatch->StopNow = true;
		CancelIoEx(pWatch->Watch->DirHandle, &pWatch->Overlapped);
		CloseHandle(pWatch->Watch->DirHandle);
		efSAFE_DELETE_ARRAY( pWatch->Watch->DirName );
		efSAFE_DELETE( pWatch->Watch );
	}
}

/// Starts monitoring a directory.
WatcherStructWin32* CreateWatch(LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, HANDLE iocp)
{
	WatcherStructWin32 * tWatch;
	size_t ptrsize = sizeof(*tWatch);
	tWatch = static_cast<WatcherStructWin32*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize));

	WatcherWin32 * pWatch = new WatcherWin32();
	tWatch->Watch = pWatch;

	pWatch->DirHandle = CreateFileW(
							szDirectory,
							GENERIC_READ,
							FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
							NULL,
							OPEN_EXISTING,
							FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
							NULL
						);

	if (pWatch->DirHandle != INVALID_HANDLE_VALUE &&
		CreateIoCompletionPort(pWatch->DirHandle, iocp, 0, 1))
	{
		pWatch->NotifyFilter = NotifyFilter;
		pWatch->Recursive = recursive;

		if (RefreshWatch(tWatch))
		{
			return tWatch;
		}
	}

	CloseHandle(pWatch->DirHandle);
	efSAFE_DELETE( pWatch->Watch );
	HeapFree(GetProcessHeap(), 0, tWatch);
	return NULL;
}

}

 #endif
